"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFunction = exports.createConfig = exports.createEndpoint = exports.isObjectOrFunction = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const findup_sync_1 = __importDefault(require("findup-sync"));
const path_2 = require("../path");
const github_1 = require("../github");
const logger_1 = require("../logger");
const lang_1 = require("../../utils/lang");
const FileSystemError_1 = require("../../models/FileSystemError");
const i18nKey = 'lib.cms.functions';
function isObjectOrFunction(value) {
    const type = typeof value;
    return value != null && (type === 'object' || type === 'function');
}
exports.isObjectOrFunction = isObjectOrFunction;
function createEndpoint(endpointMethod, filename) {
    return {
        method: endpointMethod || 'GET',
        file: filename,
    };
}
exports.createEndpoint = createEndpoint;
function createConfig({ endpointPath, endpointMethod, functionFile, }) {
    return {
        runtime: 'nodejs18.x',
        version: '1.0',
        environment: {},
        secrets: [],
        endpoints: {
            [endpointPath]: createEndpoint(endpointMethod, functionFile),
        },
    };
}
exports.createConfig = createConfig;
function writeConfig(configFilePath, config) {
    const configJson = JSON.stringify(config, null, '  ');
    fs_extra_1.default.writeFileSync(configFilePath, configJson);
}
function updateExistingConfig(configFilePath, { endpointPath, endpointMethod, functionFile }) {
    let configString;
    try {
        configString = fs_extra_1.default.readFileSync(configFilePath).toString();
    }
    catch (err) {
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.updateExistingConfig.unableToReadFile`, {
            configFilePath,
        }));
        throw new FileSystemError_1.FileSystemError({ cause: err }, {
            filepath: configFilePath,
            operation: 'read',
        });
    }
    let config;
    try {
        config = JSON.parse(configString);
    }
    catch (err) {
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.updateExistingConfig.invalidJSON`, {
            configFilePath,
        }));
        throw new FileSystemError_1.FileSystemError({ cause: err }, {
            filepath: configFilePath,
            operation: 'read',
        });
    }
    if (!isObjectOrFunction(config)) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.updateExistingConfig.errors.configIsNotObjectError`, {
            configFilePath,
        }));
    }
    if (config.endpoints) {
        if (config.endpoints[endpointPath]) {
            throw new Error((0, lang_1.i18n)(`${i18nKey}.updateExistingConfig.errors.endpointAreadyExistsError`, {
                configFilePath,
                endpointPath,
            }));
        }
        else {
            config.endpoints[endpointPath] = createEndpoint(endpointMethod, functionFile);
        }
    }
    else {
        config.endpoints = {
            [endpointPath]: createEndpoint(endpointMethod, functionFile),
        };
    }
    try {
        writeConfig(configFilePath, config);
    }
    catch (err) {
        logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.updateExistingConfig.couldNotUpdateFile`, {
            configFilePath,
        }));
        throw new FileSystemError_1.FileSystemError({ cause: err }, {
            filepath: configFilePath,
            operation: 'read',
        });
    }
}
async function createFunction(functionInfo, dest, options = {}) {
    const { functionsFolder, filename, endpointPath, endpointMethod } = functionInfo;
    const allowExistingFile = options.allowExistingFile || false;
    const ancestorFunctionsConfig = (0, findup_sync_1.default)('serverless.json', {
        cwd: (0, path_2.getCwd)(),
        nocase: true,
    });
    if (ancestorFunctionsConfig) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.createFunction.errors.nestedConfigError`, {
            ancestorConfigPath: path_1.default.dirname(ancestorFunctionsConfig),
        }));
    }
    const folderName = functionsFolder.endsWith('.functions')
        ? functionsFolder
        : `${functionsFolder}.functions`;
    const functionFile = filename.endsWith('.js') ? filename : `${filename}.js`;
    const destPath = path_1.default.join(dest, folderName);
    if (fs_extra_1.default.existsSync(destPath)) {
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.destPathAlreadyExists`, {
            path: destPath,
        }));
    }
    else {
        fs_extra_1.default.mkdirp(destPath);
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.createdDest`, {
            path: destPath,
        }));
    }
    const functionFilePath = path_1.default.join(destPath, functionFile);
    const configFilePath = path_1.default.join(destPath, 'serverless.json');
    if (!allowExistingFile && fs_extra_1.default.existsSync(functionFilePath)) {
        throw new Error((0, lang_1.i18n)(`${i18nKey}.createFunction.errors.jsFileConflictError`, {
            functionFilePath,
        }));
    }
    const result = await (0, github_1.fetchFileFromRepository)('HubSpot/cms-sample-assets', 'functions/sample-function.js', 'main');
    fs_extra_1.default.writeFileSync(functionFilePath, result);
    logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.createdFunctionFile`, {
        path: functionFilePath,
    }));
    if (fs_extra_1.default.existsSync(configFilePath)) {
        updateExistingConfig(configFilePath, {
            endpointPath,
            endpointMethod,
            functionFile,
        });
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.createdFunctionFile`, {
            path: functionFilePath,
        }));
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.success`, {
            endpointPath: endpointPath,
            folderName,
        }));
    }
    else {
        const config = createConfig({ endpointPath, endpointMethod, functionFile });
        try {
            writeConfig(configFilePath, config);
        }
        catch (err) {
            logger_1.logger.debug((0, lang_1.i18n)(`${i18nKey}.createFunction.failedToCreateFile`, {
                configFilePath,
            }));
            throw new FileSystemError_1.FileSystemError({ cause: err }, {
                filepath: configFilePath,
                operation: 'write',
            });
        }
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.createdConfigFile`, {
            path: configFilePath,
        }));
        logger_1.logger.log((0, lang_1.i18n)(`${i18nKey}.createFunction.success`, {
            endpointPath: endpointPath,
            folderName,
        }));
    }
}
exports.createFunction = createFunction;
